/*
REWRITTEN FROM SCRATCH BY XINEF, IT OWNS NOW!
*/

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "utgarde_pinnacle.h"
#include "SpellInfo.h"

enum Misc
{
	// TEXTS
    SAY_AGGRO                               = 0,
    SAY_SLAY                                = 1,
    SAY_DEATH                               = 2,
    SAY_SUMMON_BJORN                        = 3,
    SAY_SUMMON_HALDOR                       = 4,
    SAY_SUMMON_RANULF                       = 5,
    SAY_SUMMON_TORGYN                       = 6,

	// SPELLS
    SPELL_BANE_N                            = 48294,
    SPELL_BANE_H                            = 59301,
    SPELL_DARK_SLASH                        = 48292,
    SPELL_FETID_ROT_N                       = 48291,
    SPELL_FETID_ROT_H                       = 59300,
    SPELL_SCREAMS_OF_THE_DEAD               = 51750,
    SPELL_SPIRIT_BURST_N                    = 48529, // when Ranulf
    SPELL_SPIRIT_BURST_H                    = 59305, // when Ranulf
    SPELL_SPIRIT_STRIKE_N                   = 48423, // when Haldor
    SPELL_SPIRIT_STRIKE_H                   = 59304, // when Haldor

    SPELL_SUMMON_AVENGING_SPIRIT            = 48592,
    SPELL_SUMMON_SPIRIT_FOUNT               = 48386,

    SPELL_CHANNEL_SPIRIT_TO_YMIRON          = 48316,
    SPELL_CHANNEL_YMIRON_TO_SPIRIT          = 48307,

    SPELL_SPIRIT_FOUNT_N                    = 48380,
    SPELL_SPIRIT_FOUNT_H                    = 59320,

	SPELL_FLAMES							= 39199,

	// NPCS
    NPC_BJORN                               = 27303, // Near Right Boat, summon Spirit Fount
    NPC_BJORN_VISUAL                        = 27304,
    NPC_HALDOR                              = 27307, // Near Left Boat, debuff Spirit Strike on player
    NPC_HALDOR_VISUAL                       = 27310,
    NPC_RANULF                              = 27308, // Far Left Boat, ability to cast spirit burst
    NPC_RANULF_VISUAL                       = 27311,
    NPC_TORGYN                              = 27309, // Far Right Boat, summon 4 Avenging Spirit
    NPC_TORGYN_VISUAL                       = 27312,

    NPC_SPIRIT_FOUNT                        = 27339,
    NPC_AVENGING_SPIRIT                     = 27386,
};

enum Events
{
	EVENT_YMIRON_HEALTH_CHECK			= 1,
	EVENT_YMIRON_BANE					= 2,
	EVENT_YMIRON_FETID_ROT				= 3,
	EVENT_YMIRON_DARK_SLASH				= 4,
	EVENT_YMIRON_ACTIVATE_BOAT			= 5,
	EVENT_YMIRON_BJORN_ABILITY			= 6,
	EVENT_YMIRON_RANULF_ABILITY			= 7,
	EVENT_YMIRON_HALDOR_ABILITY			= 8,
	EVENT_YMIRON_TORGYN_ABILITY			= 9,
	EVENT_YMIRON_RESTORE				= 10,
};

struct ActiveBoatStruct
{
	uint32 trigger;
    uint32 npc;
    uint32 say;
    float MoveX, MoveY, MoveZ, SpawnX, SpawnY, SpawnZ, SpawnO;
};

static ActiveBoatStruct BoatStructure[4] =
{
    {NPC_RANULF,  NPC_RANULF_VISUAL,  SAY_SUMMON_RANULF,  404.379f, -335.335f, 104.756f, 413.594f, -335.408f, 107.995f, 3.157f},
    {NPC_TORGYN, NPC_TORGYN_VISUAL, SAY_SUMMON_TORGYN, 380.813f, -335.069f, 104.756f, 369.994f, -334.771f, 107.995f, 6.232f},
    {NPC_BJORN, NPC_BJORN_VISUAL, SAY_SUMMON_BJORN, 381.546f, -314.362f, 104.756f, 370.841f, -314.426f, 107.995f, 6.232f},
    {NPC_HALDOR, NPC_HALDOR_VISUAL, SAY_SUMMON_HALDOR, 404.310f, -314.761f, 104.756f, 413.992f, -314.703f, 107.995f, 3.157f}
};

class boss_ymiron : public CreatureScript
{
public:
    boss_ymiron() : CreatureScript("boss_ymiron") { }

    CreatureAI* GetAI(Creature* pCreature) const
    {
        return new boss_ymironAI (pCreature);
    }

	struct boss_ymironAI : public ScriptedAI
	{
		boss_ymironAI(Creature *pCreature) : ScriptedAI(pCreature), summons(me), summons2(me)
		{
    		pInstance = pCreature->GetInstanceScript();
		}

		InstanceScript *pInstance;
		EventMap events;
		SummonList summons;
		SummonList summons2;
		uint8 BoatNum;
		uint8 BoatOrder[4];

		void Reset()
		{
			for (uint8 i = 0; i < 4; ++i)
			{
				bool good;
				do
				{
					good = true;
					BoatOrder[i] = urand(0,3);

					for (uint8 j = 0; j < i; ++j)
						if (BoatOrder[i] == BoatOrder[j])
						{
							good = false;
							break;
						}
				}
				while (!good);
			}

			events.Reset();
			summons.DespawnAll();
			summons2.DespawnAll();
			BoatNum = 0;

			me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_DISABLE_MOVE);
			
			if(pInstance)
			{
				pInstance->SetData(DATA_KING_YMIRON, NOT_STARTED);
				pInstance->SetData(DATA_YMIRON_ACHIEVEMENT, true);

				me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
				if (pInstance->GetData(DATA_SKADI_THE_RUTHLESS) == DONE)
					me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
			}
		}

		void EnterEvadeMode()
		{
			me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
			ScriptedAI::EnterEvadeMode();
		}

		void EnterCombat(Unit* pWho)
		{
			Talk(SAY_AGGRO);
			if(pInstance)
			{
				pInstance->SetData(DATA_KING_YMIRON, IN_PROGRESS);
				if (pInstance->GetData(DATA_SKADI_THE_RUTHLESS) == DONE)
					me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
			}

			events.RescheduleEvent(EVENT_YMIRON_BANE, 18000);
			events.RescheduleEvent(EVENT_YMIRON_FETID_ROT, 8000);
			events.RescheduleEvent(EVENT_YMIRON_DARK_SLASH, 28000);
			events.RescheduleEvent(EVENT_YMIRON_HEALTH_CHECK, 1000);
		}

		void MovementInform(uint32 uiType, uint32 point)
		{
			if(uiType != POINT_MOTION_TYPE)
				return;

			if (point == 0)
			{
				Talk(BoatStructure[BoatOrder[BoatNum-1]].say);
				if (Creature *cr = me->FindNearestCreature(BoatStructure[BoatOrder[BoatNum-1]].trigger, 50.0f))
					me->CastSpell(cr, SPELL_CHANNEL_YMIRON_TO_SPIRIT, true);

				events.ScheduleEvent(EVENT_YMIRON_ACTIVATE_BOAT, 6000);
			}
		}

		void SpellHitTarget(Unit *, const SpellInfo *spellInfo)
		{
			if (spellInfo->Id == 59302 && pInstance) // Bane trigger
				pInstance->SetData(DATA_YMIRON_ACHIEVEMENT, false);
		}

		void UpdateAI(uint32 diff)
		{
			if (!UpdateVictim())
				return;

			events.Update(diff);
			if (me->HasUnitState(UNIT_STATE_CASTING))
				return;

			switch (events.GetEvent())
			{
				case EVENT_YMIRON_HEALTH_CHECK:
				{
					if (me->GetHealth() < std::max(0.0f, float(me->GetMaxHealth()*(1.0f-(IsHeroic() ? 0.2f : 0.334f)*float(BoatNum+1)))))
					{
						events.DelayEvents(12000);
						me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
						me->InterruptNonMeleeSpells(true);
						me->CastSpell(me, SPELL_SCREAMS_OF_THE_DEAD, true);
						me->GetMotionMaster()->Clear();
						me->GetMotionMaster()->MovePoint(0, BoatStructure[BoatOrder[BoatNum]].MoveX, BoatStructure[BoatOrder[BoatNum]].MoveY, BoatStructure[BoatOrder[BoatNum]].MoveZ);
						me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
						summons.DespawnAll();

						// Spawn flames in previous boat if any
						if (BoatNum) // different than 0
							if (Creature *cr = me->SummonTrigger(BoatStructure[BoatOrder[BoatNum-1]].SpawnX, BoatStructure[BoatOrder[BoatNum-1]].SpawnY, BoatStructure[BoatOrder[BoatNum-1]].SpawnZ, 0, 1800000))
							{
								cr->AddAura(SPELL_FLAMES, cr);
								summons2.Summon(cr);
							}

						BoatNum++;
					}

					events.RepeatEvent(1000);
					break;
				}
				case EVENT_YMIRON_BANE:
				{
					me->CastSpell(me, IsHeroic() ? SPELL_BANE_H : SPELL_BANE_N, false);
					events.RepeatEvent(20000+rand()%5000);
					break;
				}
				case EVENT_YMIRON_FETID_ROT:
				{
					me->CastSpell(me->GetVictim(), IsHeroic() ? SPELL_FETID_ROT_H : SPELL_FETID_ROT_N, false);
					events.RepeatEvent(10000+rand()%3000);
					break;
				}
				case EVENT_YMIRON_DARK_SLASH:
				{
					int32 dmg = me->GetVictim()->GetHealth() / 2;
					me->CastCustomSpell(me->GetVictim(), SPELL_DARK_SLASH, &dmg, 0, 0, false);
					events.RepeatEvent(30000+rand()%5000);
					break;
				}
				case EVENT_YMIRON_ACTIVATE_BOAT:
				{
					// Spawn it!
					if (Creature* king = me->SummonCreature(BoatStructure[BoatOrder[BoatNum-1]].npc, BoatStructure[BoatOrder[BoatNum-1]].SpawnX, BoatStructure[BoatOrder[BoatNum-1]].SpawnY, BoatStructure[BoatOrder[BoatNum-1]].SpawnZ, BoatStructure[BoatOrder[BoatNum-1]].SpawnO, TEMPSUMMON_CORPSE_DESPAWN, 0))
					{
						me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
						king->CastSpell(me, SPELL_CHANNEL_SPIRIT_TO_YMIRON, true);
						summons.Summon(king);
						king->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
						king->SetDisableGravity(true);
						me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
						me->GetMotionMaster()->MoveChase(me->GetVictim());
						switch(BoatOrder[BoatNum-1])
						{
							case 0: events.ScheduleEvent(EVENT_YMIRON_RANULF_ABILITY, 3000, 1); break;
							case 1: events.ScheduleEvent(EVENT_YMIRON_TORGYN_ABILITY, 3000, 1); break;
							case 2: events.ScheduleEvent(EVENT_YMIRON_BJORN_ABILITY, 3000, 1); break;
							case 3: events.ScheduleEvent(EVENT_YMIRON_HALDOR_ABILITY, 3000, 1); break;
						}
					}

					events.PopEvent();
					break;
				}
				case EVENT_YMIRON_BJORN_ABILITY:
				{
					if (Creature* sf = me->SummonCreature(NPC_SPIRIT_FOUNT, 385+rand()%10, -330+rand()%10, 104.756f, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 180000))
					{
						summons.Summon(sf);
						sf->SetSpeed(MOVE_RUN, 0.4f);
						sf->AddAura(IsHeroic() ? SPELL_SPIRIT_FOUNT_H : SPELL_SPIRIT_FOUNT_N, sf);
						sf->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
						sf->GetMotionMaster()->MoveFollow(me->GetVictim(), 0, rand_norm()*M_PI*2);
					}
					events.PopEvent();
					break;
				}
				case EVENT_YMIRON_HALDOR_ABILITY:
				{
					me->CastSpell(me->GetVictim(), IsHeroic() ? SPELL_SPIRIT_STRIKE_H : SPELL_SPIRIT_STRIKE_N, false);
					events.RepeatEvent(5000);
					break;
				}
				case EVENT_YMIRON_RANULF_ABILITY:
				{
					me->CastSpell(me, IsHeroic() ? SPELL_SPIRIT_BURST_H : SPELL_SPIRIT_BURST_N, false);
					events.RepeatEvent(10000);
					break;
				}
				case EVENT_YMIRON_TORGYN_ABILITY:
				{
					for(uint8 i = 0; i < 4; ++i)
					{
						if (Creature* as = me->SummonCreature(NPC_AVENGING_SPIRIT, me->GetPositionX()+rand()%10, me->GetPositionY()+rand()%10, me->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 30000))
						{
							summons.Summon(as);
							as->SetInCombatWithZone();
						}
					}
					events.RepeatEvent(15000);
					break;
				}

			}

			DoMeleeAttackIfReady();  
		}

		void JustDied(Unit* pKiller)
		{
			Talk(SAY_DEATH);
			summons.DespawnAll();
			summons2.DespawnAll();

			if(pInstance)
				pInstance->SetData(DATA_KING_YMIRON, DONE);
		}

		void KilledUnit(Unit* pVictim)
		{
			if (urand(0,1))
				return;

			Talk(SAY_SLAY);
		}
	};
};

void AddSC_boss_ymiron()
{
    new boss_ymiron();
}